package uk.ac.bath.genetic.mover;

import java.util.logging.Level;
import java.util.logging.Logger;
import uk.ac.bath.genetic.bool.LogicalMachine;
import uk.ac.bath.genetic.component.Point2DInt;
import uk.ac.bath.genetic.component.FoodSupply;
import uk.ac.bath.genetic.base.Point;
import uk.ac.bath.genetic.base.Cell;
import uk.ac.bath.genetic.mover.Environment;
import uk.ac.bath.genetic.base.VectorState;

import uk.ac.bath.util.*;
import java.util.*;
import uk.ac.bath.genetic.base.EnvironmentIF;

public class MoverCellCT extends MoverCell {

    final static int lOut = 0;
    final static int rOut = 1;
    final static int dOut = 2;
    final static int uOut = 3;
    final static int doCloneOut = 4;
    final static int giveCloneOut = 5;
    final static int doSexOut = 6;
    final static int fitIn = -1;
    final static int foodIn1 = -6;
    final static int foodIn2 = -2;
    public static Vector<Tweakable> tweaks = new Vector<Tweakable>();
    public static TweakableDouble cellMutateProb =
            new TweakableDouble(tweaks, 0.0, 1.0, 0.001, 0.001, "MutateProb");
    public static TweakableInt tickCost =
            new TweakableInt(tweaks, 0, 200, 3, "TickCost");
    public static TweakableInt moveCost =
            new TweakableInt(tweaks, 0, 200, 2, "MoveCost");
    public static TweakableInt maxBite =
            new TweakableInt(tweaks, 0, 200, 5, "MaxBite");
    public static TweakableDouble cloneCostFact =
            new TweakableDouble(tweaks, 0.0, 10.0, 1.0, 0.01, "CloneCostPerOp");

    public Collection<Tweakable> getTweaks() {
        return tweaks;
    }

    public MoverCellCT(LogicalMachine machine,
            Point point,
            double fitness,
            int id,
            int generation,
            int mutations,
            String tag) {
        super(machine, point, fitness, id, generation, mutations, tag);
    }

    public Cell cloneToPoint(Point p, double fitness) throws CloneNotSupportedException {
        return (new MoverCellCT((LogicalMachine) (machine.clone()), p,
                fitness, id_,
                generation + 1, mutationCount, tag));

    }

    public MoverCellCT() {
        super(null, null, 0, 0, -1, 0, "MCT Proto");
    }

    public void tickImpl(EnvironmentIF env1) {

        MoverEnvironment env = (MoverEnvironment) env1;
        FoodSupply food = env.getFoodSupply();
        age++;
        if (fitness <= 0.0) {
            return;
        }

        boolean foodFlag = food.getFoodAt(point) > 0.0;
        fitness += food.eatFoodAt(point, maxBite.getNumber().doubleValue());

        if (env.free() > 0.0 && at(doCloneOut).isOn()) {
            Point pNew = env.randomEmptyAdjacent(point);
            if (pNew != null) {
                fitness -= cloneCostFact.doubleValue() * size;
                if (fitness < 0.0) {
                    return; // I'm dead
                }
                double fitAsk = at(giveCloneOut).doubleValue() * fitness;
                //		assert fitAsk >= 0.0;
                if (fitAsk > fitness) {
                    fitAsk = fitness;
                }
                Cell newCell;
                try {
                    newCell = cloneToPoint(pNew, fitAsk);

                    fitness -= fitAsk;
                    if (MyRandom.nextDouble() <
                            cellMutateProb.getNumber().doubleValue()) {
                        //           newCell.mutate(pallete);
                        env.mutateCell(newCell);
                    }
                    env.addCell(newCell);
                } catch (CloneNotSupportedException ex) {
                    Logger.getLogger(MoverCellCT.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }


        double fVal;
        if (foodFlag) {
            fVal = 1.0;
        } else {
            fVal = 0.0;
        }

        for (int i = foodIn1; i <= foodIn2; i++) {
            at(i).set(fVal);
        }


        at(fitIn).set(fitness / MoverCellCTFactory.initFit.getNumber().doubleValue());

        machine.tick();



        VectorState state = machine.getState();


        Point2DInt newPoint = (Point2DInt) point.clone();

        int n = 0;
        int dx = 0;
        if (state.at(lOut).isOn()) {
            newPoint.x++;
            n++;
        }

        if (state.at(rOut).isOn()) {
            newPoint.x--;
            n++;
        }

        if (state.at(dOut).isOn()) {
            newPoint.y++;
            n++;
        }

        if (state.at(uOut).isOn()) {
            newPoint.y--;
            n++;
        }


        fitness -= tickCost.getNumber().doubleValue() + n * moveCost.getNumber().doubleValue();

        if (!env.getSpace().contains(newPoint)) {
            fitness = -1;
            return;
        }


        Cell c = null;

        if (!newPoint.equals(point)) {
            c = env.cellAt(newPoint);
            if (c == null) {
                prevPoint.set(point);
                point.set(newPoint);
                env.moveCell(this);
                return;
            } else {
                //		collideWith(c,env);
            }
        }
    }

    public void collideWith(Cell c, MoverEnvironment env) throws CloneNotSupportedException {
        MoverCellCT ct = (MoverCellCT) c;
        if (!doSex()) {
            return;
        }
        if (!ct.doSex()) {
            return;
        }
        Point pNew = env.randomEmptyAdjacent(point);

        if (pNew == null) {
            return;
        }

        //	System.out.println(" Do it to me ");

        LogicalMachine m = ((LogicalMachine) machine).splice((LogicalMachine) c.getMachine());


        fitness -= cloneCostFact.doubleValue() * m.size();
        if (fitness < 0) {
            return; // I'm dead
        }
        double fitAsk = at(giveCloneOut).doubleValue();
        fitAsk = Math.min(fitness, fitAsk);
        fitAsk = Math.max(0.0, fitAsk);


        Cell newCell = new MoverCellCT(m, pNew, fitAsk, nextId(), 100, 0, tag + "+" + c.getTag());


        fitness -= fitAsk;

        if (MyRandom.nextDouble() <
                cellMutateProb.getNumber().doubleValue()) {
            // newCell.mutate(pallete);
            env.mutateCell(newCell);
        }

        env.addCell(newCell);

    }

    boolean doSex() {
        return at(doSexOut).isOn();
    }
}




